/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xos_ffmpeg.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../helper_objs/head.h"
#include "../data_struct/pool.h"
#include "../settings/head.h"
#include "../config/head.h"
#include "../tools/head.h"
#include "../impl/head.h"
#include "../msg/head.h"
#include "../xos/head.h"
#include "sdl2_yuv.h"

namespace xos_ffmpeg
{

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 
    sdl2_yuv::sdl2_yuv()
    {
        init_data();
    }

    sdl2_yuv::~sdl2_yuv()
    {
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    void SDLCALL sdl2_yuv::static_fill_audio( void * udata, Uint8 * stream, int len )
    {
        sdl2_yuv & obj = *(sdl2_yuv *)udata;
        obj.fill_audio( stream, len );
    }

    int sdl2_yuv::static_init()
    {
        int ret = 0;

        if( 0 == ret )
        {
            ret = SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO );
            if( 0 != ret )
            {
                printf( "Could not initialize SDL - %s\n", SDL_GetError() );
            }
        }

        return ret;
    }

    int sdl2_yuv::static_term()
    {
        int ret = 0;
        SDL_Quit();
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int sdl2_yuv::load_file( xos_box::i_list * pList, const char * lpszFile )
    {
        int ret = 0;

        xos::i_file * pFile = mgr::container()->xos()->file();

        if( 0 == ret )
        {
            char file[4096] = { 0 };
            tools::full_path( file, sizeof( file ), lpszFile );
            ret = pFile->open( file, xos::i_file::XOS_FILE_READ, xos::i_file::XOS_SHARE_READ, xos::i_file::XOS_FILE_OPEN );
        }

        if( 0 == ret )
        {
            pFile->read( pList );
        }

        xos_stl::release_interface( pFile );

        return ret;
    }

    int sdl2_yuv::tanslate_audio_fmt()
    {
        int ret = 0;

        xos_box::i_big_buf * pBigBuf = mgr::container()->box()->big_buf();
        xos_box::i_buf * pSrcBuf = 0;
        xos_box::i_list * pList = mgr::container()->box()->list();
        xos_box::i_list_iter * pIter = m_pAudioList->begin();

        SDL_AudioSpec & obtained = m_audio_obtained_spec;
        SDL_AudioSpec & desired = m_audio_desired_spec;
        SDL_AudioCVT cvt = { 0 };

        if( 0 == ret )
        {
            int r = SDL_BuildAudioCVT( &cvt, desired.format, desired.channels, desired.freq, obtained.format, obtained.channels, obtained.freq );
            if( r < 0 )
            {
                printf( "不支持此格式转换: %s\n", SDL_GetError() );
                ret = 1;
            }
        }

        while( ( 0 == ret ) && ( pSrcBuf = (xos_box::i_buf *)pIter->next() ) )
        {
            cvt.buf = (Uint8 *)pBigBuf->get_data( 0, 0, 0, 0 );
            cvt.len = pSrcBuf->get_len( 0 );
            if( cvt.len * cvt.len_mult > pBigBuf->get_buf_size( 0 ) )
            {
                printf( "转换失败: %s\n", "size to long" );
                ret = 1;
                break;
            }
            pBigBuf->reset();
            pBigBuf->add_data( pSrcBuf, true );
            {
                int r = SDL_ConvertAudio( &cvt );
                if( r < 0 )
                {
                    printf( "转换失败: %s\n", SDL_GetError() );
                    ret = 2;
                    break;
                }
            }
            pBigBuf->set_len( cvt.len_cvt );
            pList->add_data( pBigBuf, true );
        }

        if( 0 == ret )
        {
            m_pAudioList->swap( pList );
        }

        xos_stl::release_interface( pBigBuf );
        xos_stl::release_interface( pIter );
        xos_stl::release_interface( pList );

        return ret;
    }

    int sdl2_yuv::sdl_msg_proc( const SDL_Event & event )
    {
        int ret = 0;

        switch( event.type )
        {
        case SDL_WINDOWEVENT:
            {
                SDL_GetWindowSize( screen, &cur_screen_w, &cur_screen_h );
                update( false );
            }
            break;
        case SDL_QUIT:
            {
                m_bRunning = false;
                helper_main::get()->notify( MAIN_QUITTING );
            }
            break;
        }

        return ret;
    }

    int sdl2_yuv::fill_audio( Uint8 * stream, int len )
    {
        int ret = 0;
        SDL_memset( stream, 0, len );
        if( !m_bRunning || ( 0 == m_pAudioList->size() ) )
        {
            return ret;
        }
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();
        char * lpszBuf = pBuf->get_data( 0, 0, 0, 0 );
        int nLen = m_pAudioList->get_data( lpszBuf, len );
        if( nLen > 0 )
        {
            SDL_MixAudioFormat( stream, (Uint8 *)lpszBuf, m_audio_obtained_spec.format, nLen, SDL_MIX_MAXVOLUME );
        }
        pBuf->set_len( nLen );
        m_pAudioList->push_back( pBuf );
        pBuf = 0;
        return ret;
    }

    int sdl2_yuv::update( bool bForceUpdate )
    {
        int ret = 0;
        if( !m_bRunning )
        {
            return ret;
        }
        if( ( cur_screen_w != screen_w ) || ( cur_screen_h != screen_h ) )
        {
            screen_w = cur_screen_w;
            screen_h = cur_screen_h;
            bForceUpdate = true;
        }
        if( bForceUpdate )
        {
            //FIX: If window is resize
            SDL_Rect sdlRect = { 0 };
            sdlRect.x = 0;
            sdlRect.y = 0;
            sdlRect.w = screen_w;
            sdlRect.h = screen_h;
            SDL_RenderClear( sdlRenderer );
            SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect );
            SDL_RenderPresent( sdlRenderer );
        }
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int sdl2_yuv::init_data()
    {
        int ret = 0;

        // audio device id
        mgr::container()->crt()->memset( &m_audio_obtained_spec, 0, sizeof( m_audio_obtained_spec ) );
        mgr::container()->crt()->memset( &m_audio_desired_spec, 0, sizeof( m_audio_desired_spec ) );
        m_nAudioDeviceId = -1;

        m_bRunning = false;

        //IYUV: Y + U + V  (3 planes)
        //YV12: Y + V + U  (3 planes)
        pixformat = SDL_PIXELFORMAT_IYUV;

        screen = 0;

        cur_screen_w = 500;
        cur_screen_h = 500;
        screen_w = 500;
        screen_h = 500;
        pixel_w = 320;
        pixel_h = 180;
        bpp = 12;

        sdlRenderer = 0;
        sdlTexture = 0;
        buffer = 0;

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int sdl2_yuv::notify()
    {
        SDL_Event event;
        event.type = SDL_USEREVENT;
        SDL_PushEvent( &event );
        return 0;
    }

    int sdl2_yuv::heart()
    {
        int ret = 0;

        if( !m_bRunning )
        {
            return ret;
        }
        {
            int nLen = pixel_w * pixel_h * bpp / 8;
            char * lpszBuf = (char *)buffer;
            m_pVideoList->get_data( lpszBuf, nLen );
            m_pVideoList->add_data( lpszBuf, nLen, true );
        }
        {
            SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w );
            update( true );
        }

        return ret;
    }

    int sdl2_yuv::wait()
    {
        int ret = 0;

        SDL_Event event = { 0 };
        SDL_WaitEvent( &event );

        if( SDL_USEREVENT != event.type )
        {
            sdl_msg_proc( event );
            ret = 1;
        }

        return ret;
    }

    int sdl2_yuv::init()
    {
        int ret = 0;

        if( 0 == ret )
        {
            //SDL_AudioSpec
            SDL_AudioSpec & desired = m_audio_desired_spec;
            desired.freq = 44100;
            desired.format = AUDIO_S16SYS;
            desired.channels = 2;
            desired.silence = 0;
            desired.samples = 1024;
            desired.callback = static_fill_audio;
            desired.userdata = this;
            m_nAudioDeviceId = SDL_OpenAudioDevice( NULL, 0, &desired, &m_audio_obtained_spec, SDL_AUDIO_ALLOW_ANY_CHANGE );
            if( m_nAudioDeviceId <= 0 )
            {
                printf( "can't open audio = %s\n", SDL_GetError() );
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            m_pAudioList = mgr::container()->box()->list();
            ret = load_file( m_pAudioList, "NocturneNo2inEflat_44.1k_s16le.pcm" );
            if( 0 == ret )
            {
                ret = tanslate_audio_fmt();
            }
        }

        if( 0 == ret )
        {
            m_pVideoList = mgr::container()->box()->list();
            ret = load_file( m_pVideoList, "test_yuv420p_320x180.yuv" );
        }

        if( 0 == ret )
        {
            screen = SDL_CreateWindow( "Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE );
            if( !screen )
            {
                printf( "SDL: could not create window - exiting:%s\n", SDL_GetError() );
                ret = 2;
            }
        }

        if( 0 == ret )
        {
            SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" );
            sdlRenderer = SDL_CreateRenderer( screen, -1, 0 );
            if( !sdlRenderer )
            {
                ret = 4;
            }
        }

        if( 0 == ret )
        {
            sdlTexture = SDL_CreateTexture( sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h );
            if( !sdlTexture )
            {
                ret = 5;
            }
        }

        if( 0 == ret )
        {
            xos_stl::init_array( buffer, pixel_w * pixel_h * bpp / 8 );
            if( !buffer )
            {
                ret = 3;
            }
        }

        if( 0 == ret )
        {
            m_bRunning = true;
            SDL_PauseAudioDevice( m_nAudioDeviceId, 0 );
        }

        return ret;
    }

    int sdl2_yuv::term()
    {
        int ret = 0;
        if( m_nAudioDeviceId > 0 )
        {
            SDL_CloseAudioDevice( m_nAudioDeviceId );
            m_nAudioDeviceId = -1;
        }
        xos_stl::release_interface( m_pVideoList );
        xos_stl::release_interface( m_pAudioList );
        xos_stl::term_array( buffer );
        init_data();
        return ret;
    }

} // xos_ffmpeg
